package PLCSymbolAndScope;

import PLCSymbolAndScope.PLCScope.PLCScope;
import PLCSymbolAndScope.PLCSymbolTables.PLCSymbolTable;
import PLCSymbolAndScope.PLCSymbolTables.PLCTotalSymbolTable;
import PLCSymbolAndScope.PLCSymbols.*;

import java.util.Iterator;
import java.util.Stack;

public class PLCSymbolTableGeneratorNew {

    //global作用域
    public PLCScope globalScope;

    //global符号表
    public PLCSymbolTable globalSymbolTable;

    //作用域栈
    public Stack<PLCScope> scopeStack = new Stack<>();

    //总符号表
    public PLCTotalSymbolTable totalSymbolTable;

    //id生成器
    public IDGenerator idGenerator;

    //符号表构造器初始化
    public PLCSymbolTableGeneratorNew() {

        //初始化ID生成器
        this.idGenerator = new IDGenerator();

        //初始化总符号表
        this.totalSymbolTable = new PLCTotalSymbolTable();

        //初始化global符号表
        this.globalSymbolTable = new PLCSymbolTable(this.idGenerator.outPutIDNum());

        //初始化global作用域
        this.globalScope = new PLCScope(this.idGenerator.outPutIDNum());

        //在global scope中设置符号表引用
        this.globalScope.scopeSymbolTable = this.globalSymbolTable;

        //在globalsymboltable中设置作用域引用
        this.globalSymbolTable.tableScope = this.globalScope;

        //TODO:加到构造方法里
        //将全局符号表添加到总表相应的Map下
        this.totalSymbolTable.totalTable.put(this.globalSymbolTable.tableId, this.globalSymbolTable);

        //将全局作用域添加到总表相应的Map下
        this.totalSymbolTable.totalScopeTable.put(this.globalScope.scopeID, this.globalScope);

        //将global作用域压栈
        this.scopeStack.push(this.globalScope);

    }

    //TODO:createClassSymbol在Symbol的构造方法中调用idGenerator的静态方法
    public PLCClassDeclSymbol createClassSymbol(String name){
        //创建class符号
        PLCClassDeclSymbol classSymbol = new PLCClassDeclSymbol(name);

        //设置唯一ID
        classSymbol.symbolId = this.idGenerator.outPutIDNum();

        return classSymbol;
    }

    //TODO：符号创建时的动作统一到一个函数内，作用域创建也相同
    //将符号声明加入到作用域栈栈顶的符号表中 TODO: 使用异常类报错 不用返回值boolean报错，下面相同
    private boolean addSymbolToStackPeekTable(PLCSymbol plcSymbol){
        //
        this.scopeStack.peek().scopeSymbolTable.symbolIDHashMap.put(plcSymbol.symbolId, plcSymbol);

        this.scopeStack.peek().scopeSymbolTable.symbolNameHashMap.put(plcSymbol.name, plcSymbol);

        return false;
    }

    //将符号加入到总表下
    private boolean addSymbolToTotalSymbolTable(PLCSymbol plcSymbol){

        this.totalSymbolTable.totalSymbolTable.put(plcSymbol.symbolId, plcSymbol);

        return false;
    }

    //将符号声明加入所属符号表以及总表中
    public boolean addSymbolToSymbolTable(PLCSymbol plcSymbol){
        //将符号声明加入到作用域栈栈顶的符号表中
        this.addSymbolToStackPeekTable(plcSymbol);

        //将符号加入总表下
        this.addSymbolToTotalSymbolTable(plcSymbol);

        return false;
    }

    //创建符号表以及对应的作用域(创建符号表和作用域对象设置ID以及设置他们之间关系)
    public PLCSymbolTable createTableAndScope(PLCISTypeDeclType plcisTypeDeclType){

        //创建符号表(设置对应的符号对象)
        PLCSymbolTable plcSymbolTable = new PLCSymbolTable(plcisTypeDeclType);//table -> class 1

        //设置符号对象的符号表引用
        plcisTypeDeclType.importSymbolTable = plcSymbolTable;//class -> table 2

        //设置符号表ID
        plcSymbolTable.tableId = this.idGenerator.outPutIDNum();

        //创建对应的作用域(设置作用域对应的符号表和符号引用)
        PLCScope plcScope = new PLCScope(plcisTypeDeclType, plcSymbolTable);//scope -> table 3| scope -> class 4

        //设置scopeID
        plcScope.scopeID = this.idGenerator.outPutIDNum();

        //设置符号表对应的作用域引用
        plcSymbolTable.tableScope = plcScope;     //table -> scope 5

        //设置所引入的作用域引用
        plcisTypeDeclType.importScope = plcScope; //class -> scope 6

        //返回符号表
        return plcSymbolTable;

    }

    //设置符号向所属符号表的引用
    public boolean setSymbolToDeclTableRef(PLCSymbol plcSymbol, PLCSymbolTable plcSymbolTable){
        //
        plcSymbol.declSymbolTable =  plcSymbolTable; //symbol -> table

        return false;
    }

    //设置符号向所属作用域的引用
    public boolean setSymbolToLocalScopeRef(PLCSymbol plcSymbol, PLCScope plcScope){

        plcSymbol.localScope = plcScope;

        return false;
    }

    //将作用域压入作用域栈
    public boolean pushScopeToStack(PLCScope plcScope){

        this.scopeStack.push(plcScope);

        return false;
    }

    //设置作用域的父作用域为栈顶作用域
    public boolean setPeekScopeToParentScope(PLCScope plcScope){
        plcScope.baseScope = this.scopeStack.peek();

        return false;
    }

    //创建临时符号（作为访问器中factor函数返回符号信息的暂时载体）
    public PLCSymbol createTempSymbol(){

        return new PLCSymbol();

    }

    //创建临时variable符号（作为访问器中factor函数返回符号信息的暂时载体）TODO：没必要
    public PLCVariable createTempVariableSymbol(){

        return new PLCVariable();

    }

    //创建临时string符号
    public PLCStringDeclSymbol createTempStringSymbol(){

        return new PLCStringDeclSymbol();

    }

    //TODO:只需要在PLCScope下写一个find方法，查找过程如下：递归调用父Scope和命名空间scope的find方法


    //利用当前作用域栈以及相应的符号表查询符号
    public PLCSymbol searchSymbolByScopeStack(String name){
        //拷贝作用域栈
        Stack<PLCScope> tempScopeStack = this.scopeStack;

        while(!tempScopeStack.isEmpty()){
            //
            PLCScope plcScope = tempScopeStack.peek();

            //查询该作用域的hash表
            if(plcScope.scopeSymbolTable.searchSymbolByName(name) != null) {

                return plcScope.scopeSymbolTable.searchSymbolByName(name);

            }



            //在该作用域下找不到
            tempScopeStack.pop();

        }
        return null;
    }
}